/*
Pyxis 2.0
Copyright 2010-2011 Thomas W. Holtquist

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

using System;
using System.Collections;

using Microsoft.SPOT;
using Microsoft.SPOT.Presentation.Media;

using Skewworks.Pyxis.EXT;
using Skewworks.Pyxis.GUI;

namespace Skewworks.Pyxis.Kernel
{

    internal class MenuBar : MarshalByRefObject
    {

        #region Variables

        protected internal MenuTray _tray = null;

        private ArrayList _MenuItems;
        private MenuItem _selMenu = null;

        private Color SelBottom = ColorUtility.ColorFromRGB(184, 214, 248);
        private Color NrmlTop = ColorUtility.ColorFromRGB(241, 241, 241);
        private Color DrkGray = ColorUtility.ColorFromRGB(112, 112, 112);

        protected internal PyxisAPI API;

        private bool _bSuspended = false;

        internal Bitmap _buffer;
        private Desktop _parent;

        private Image32 _pyxisIcon = new Image32(Resources.GetBytes(Resources.BinaryResources.logo));

        #endregion

        #region Constructor

        public MenuBar(Desktop owner, PyxisAPI api, bool bShowTime)
        {
            _buffer = new Bitmap(AppearanceManager.ScreenWidth, 22);
            owner.RenderBkg(_buffer);
            _MenuItems = new ArrayList();
            _parent = owner;
            API = api;
            _tray = new MenuTray(this);
        }

        #endregion

        #region Properties

        public int count
        {
            get { return _MenuItems.Count; }
        }

        public bool Suspended
        {
            get { return _bSuspended; }
            set
            {
                if (_bSuspended = value) return;
                _bSuspended = value;
            }
        }

        #endregion

        #region Public Methods

        public void add(MenuItem menu)
        {
            menu.Container = this;
            _MenuItems.Add(menu);
            if (!_bSuspended) Render(true);
        }

        public void remove(MenuItem menu)
        {
            _MenuItems.Remove(menu);
            if (!_bSuspended) Render(true);
        }

        public void remove(int index)
        {
            _MenuItems.RemoveAt(index);
            if (!_bSuspended) Render(true);
        }

        public void clear()
        {
            _MenuItems.Clear();
            if (!_bSuspended) Render(true);
        }

        #endregion

        #region Touch

        internal bool InvokeTouchDown(point e)
        {
            MenuItem mnu;

            // Check top-level first
            for (int i = 0; i < _MenuItems.Count; i++)
            {
                mnu = (MenuItem)_MenuItems[i];
                if (mnu.Bounds.contains(e))
                {

                    _selMenu = mnu;

                    // In case we have any siblings open
                    if (_selMenu.Parent != null)
                    {
                        if (!collapseMenu(_selMenu.Parent, true))
                        {
                            RenderMenuItem(mnu);
                        }
                    }
                    else
                    {
                        collapseMenus(null);
                        RenderMenuItem(mnu);
                    }

                    return true;
                }

                // Check children
                if (mnu.Expanded)
                {
                    mnu = getClickedMenu(mnu, e);
                    if (mnu != null)
                    {
                        _selMenu = mnu;

                        // In case we have any siblings open
                        if (_selMenu.Parent != null)
                        {
                            if (!collapseMenu(_selMenu.Parent, true))
                            {
                                RenderMenuItem(mnu);
                            }
                        }
                        else
                            RenderMenuItem(mnu);

                        return true;
                    }
                }

            }

            // Check System Tray
            return _tray.InvokeTouchDown(e);
        }

        internal bool InvokeTouchUp(point e)
        {
            MenuItem mnuPrev = _selMenu;

            try
            {

                // Check System Tray
                if (_tray.InvokeTouchUp(e))
                    return true;

                // If we don't have a selected menu there's no point checking
                if (_selMenu == null)
                {
                    collapseMenus(null);
                    return false;
                }

                if (_selMenu.Bounds.contains(e))
                {
                    if (_selMenu.ItemCount > 0)
                    {
                        _selMenu.Expanded = !_selMenu.Expanded;
                        if (!_selMenu.Expanded)
                        {
                            _selMenu = null;
                            collapseMenu(mnuPrev, true);
                        }
                        else
                        {
                            _selMenu = null;
                            RenderMenuItem(mnuPrev);
                            if (mnuPrev.Parent == null)
                            {
                                drawExpandedMenu(mnuPrev, mnuPrev.Bounds.X, mnuPrev.Bounds.Y + 22);
                            }
                            else
                            {
                                rect rmnu = getExpandedMenuRect(mnuPrev.Parent, 0, 0);
                                drawExpandedMenu(mnuPrev, mnuPrev.Bounds.X + rmnu.Width - 5, mnuPrev.Bounds.Y - 2);
                            }
                            //API.ScreenBuffer.Flush();
                        }
                    }
                    else
                    {
                        _selMenu = null;
                        collapseMenus(null);
                        Render(true);
                        mnuPrev.invokeTap(mnuPrev);
                    }

                    return true;
                }
                else
                {
                    _selMenu = null;
                    RenderMenuItem(mnuPrev);
                    return false;
                }

            }
            catch (Exception)
            {
                // Exceptions can happed if an app unloads from a menu click
                return true;
            }
        }

        #endregion

        #region GUI

        /// <summary>
        /// Draws a collection of submenu items
        /// </summary>
        /// <param name="mnu">Top-level MenuItem</param>
        /// <param name="x">Draw destination X</param>
        /// <param name="y">Draw destination Y</param>
        private void drawExpandedMenu(MenuItem mnu, int x, int y)
        {
            MenuItem m;
            int i;
            int offsetX = (menuHasIcons(mnu)) ? 20 : 0;
            rect rmnu = getExpandedMenuRect(mnu, x, y);
            x = rmnu.X;
            y = rmnu.Y;

            int exW, exH;
            FontManager.Arial.ComputeExtent(">", out exW, out exH);

            // Draw the outline of the menu
            API.ScreenBuffer.DrawRectangle(DrkGray, 1, x, y, rmnu.Width, rmnu.Height, 0, 0, NrmlTop, 0, 0, NrmlTop, 0, 0, 256);
            API.ScreenBuffer.DrawRectangle(Color.White, 1, x + 1, y + 1, rmnu.Width - 2, rmnu.Height - 2, 0, 0, Color.White, 0, 0, Color.White, 0, 0, 0);

            // Draw each text item
            x = x + 4;
            y = y + 2;
            for (i = 0; i < mnu.ItemCount; i++)
            {
                m = mnu.Item(i);

                if (m.Visible)
                {
                    if (m.Text == "-")
                    {
                        API.ScreenBuffer.DrawLine(Color.White, 1, x, y + 2, x + rmnu.Width - 8, y + 2);
                        API.ScreenBuffer.DrawLine(DrkGray, 1, x, y + 3, x + rmnu.Width - 8, y + 3);

                    }
                    else
                    {
                        if (m == _selMenu)
                        {
                            API.ScreenBuffer.DrawRectangle(Color.White, 0, x - 2, y, rmnu.Width - 4, m.Height, 0, 0, SelBottom, 0, 0, SelBottom, 0, 0, 256);
                            API.ScreenBuffer.DrawText(m.Text, FontManager.Arial, Color.White, x + offsetX, y + 2);
                            m.DrawIcon(API.ScreenBuffer, x, y);
                            if (m.ItemCount > 0) API.ScreenBuffer.DrawText(">", FontManager.Arial, Color.White, x + rmnu.Width - exW - 8, y + 2);
                        }
                        else
                        {
                            API.ScreenBuffer.DrawText(m.Text, FontManager.Arial, (m.Enabled) ? Color.Black : Colors.Gray, x + offsetX, y + 2);
                            m.DrawIcon(API.ScreenBuffer, x, y);
                            if (m.ItemCount > 0) API.ScreenBuffer.DrawText(">", FontManager.Arial, (m.Enabled) ? Color.Black : Colors.Gray, x + rmnu.Width - exW - 8, y + 2);
                        }
                        m.Bounds = new rect(x, y, rmnu.Width - 8, m.Height);
                    }
                    y += m.Height;


                    if (m.Expanded)
                    {
                        drawExpandedMenu(m, x + rmnu.Width - 5, y - m.Height - 2);
                    }
                }
            }

            API.ScreenBuffer.Flush(rmnu.X, rmnu.Y, rmnu.Width, rmnu.Height);

        }

        public void Render()
        {
            Render(false);
        }

        private void Render(bool Flush)
        {
            int i;
            MenuItem mnu;
            int x = 0;

            API.ScreenBuffer.SetClippingRectangle(0, 0, AppearanceManager.ScreenWidth, AppearanceManager.ScreenHeight);

            if (_buffer == null)
            {
                _buffer = new Bitmap(AppearanceManager.ScreenWidth, 22);
                _parent.RenderBkg(_buffer);
            }

            // Draw translucent bar
            API.ScreenBuffer.DrawImage(0, 0, _buffer, 0, 0, _buffer.Width, 22);
            API.ScreenBuffer.DrawRectangle(Color.Black, 0, 0, 0, AppearanceManager.ScreenWidth - _tray.Width, 22, 0, 0, Color.Black, 0, 0, Color.Black, 0, 0, 150);

            // Draw Tray
            _tray.Render();

            // Draw Pyxis Menu
            mnu = (MenuItem)_MenuItems[0];
            mnu.Bounds = new rect(x, 0, 24, 22);
            if (mnu == _selMenu)
                API.ScreenBuffer.DrawRectangle(SelBottom, 0, x, 0, 24, 22, 0, 0, SelBottom, 0, 0, SelBottom, 0, 0, 256);
            _pyxisIcon.draw(API.ScreenBuffer, x + 4, 2);
            x += 26;

            // Draw children
            for (i = 1; i < _MenuItems.Count; i++)
            {
                mnu = (MenuItem)_MenuItems[i];
                if (mnu.Visible)
                {
                    // Always update Bounds on render, an item could have been removed
                    mnu.Bounds = new rect(x, 0, mnu.Width, 22);

                    if (mnu == _selMenu)
                        API.ScreenBuffer.DrawRectangle(SelBottom, 0, x, 0, mnu.Width, 22, 0, 0, SelBottom, 0, 0, SelBottom, 0, 0, 256);

                    if (mnu.icon != null) API.ScreenBuffer.DrawImage(x + 4, 2, mnu.icon, 0, 0, mnu.icon.Width, mnu.icon.Height);
                    if (mnu.Text != String.Empty) API.ScreenBuffer.DrawText(mnu.Text, FontManager.Arial, (mnu.Enabled) ? Color.White : Colors.DarkGray, x + mnu.IconWidth + 6, 3);

                    else
                    {
                        API.ScreenBuffer.DrawText(mnu.Text, FontManager.Arial, (mnu.Enabled) ? Color.White : Colors.DarkGray, x + 4, 3);
                    }

                    x += mnu.Width + 8;
                }
            }

            // Draw expanded items
            for (i = 0; i < _MenuItems.Count; i++)
            {
                mnu = (MenuItem)_MenuItems[i];
                if (mnu.Expanded && mnu.Visible) drawExpandedMenu(mnu, mnu.Bounds.X, 22);
            }

            if (Flush) API.ScreenBuffer.Flush();
        }

        private void RenderMenuItem(MenuItem mnu)
        {
            rect Bounds = mnu.Bounds;

            if (!mnu.Visible) return;

            API.ScreenBuffer.SetClippingRectangle(0, 0, AppearanceManager.ScreenWidth, AppearanceManager.ScreenHeight);

            // Top level items are rendered differently
            if (mnu.Parent == null)
            {
                // Draw translucent bar
                API.ScreenBuffer.DrawImage(Bounds.X, Bounds.Y, _buffer, Bounds.X, Bounds.Y, Bounds.Width, 22);
                API.ScreenBuffer.DrawRectangle(Color.Black, 0, Bounds.X, Bounds.Y, Bounds.Width, 22, 0, 0, Color.Black, 0, 0, Color.Black, 0, 0, 150);

                // Pyxis menu is draw differently
                if (mnu == (MenuItem)_MenuItems[0])
                {
                    if (mnu == _selMenu)
                        API.ScreenBuffer.DrawRectangle(SelBottom, 0, Bounds.X, 0, 24, 22, 0, 0, SelBottom, 0, 0, SelBottom, 0, 0, 256);
                    _pyxisIcon.draw(API.ScreenBuffer, Bounds.X + 4, 2);
                }
                else
                {
                    if (mnu == _selMenu)
                        API.ScreenBuffer.DrawRectangle(SelBottom, 0, Bounds.X, 0, mnu.Width, 22, 0, 0, SelBottom, 0, 0, SelBottom, 0, 0, 256);

                    if (mnu.icon != null) API.ScreenBuffer.DrawImage(Bounds.X + 4, 2, mnu.icon, 0, 0, mnu.icon.Width, mnu.icon.Height);
                    if (mnu.Text != String.Empty) API.ScreenBuffer.DrawText(mnu.Text, FontManager.Arial, (mnu.Enabled) ? Color.White : Colors.DarkGray, Bounds.X + mnu.IconWidth + 6, 3);

                    else
                    {
                        API.ScreenBuffer.DrawText(mnu.Text, FontManager.Arial, (mnu.Enabled) ? Color.White : Colors.DarkGray, Bounds.X + 4, 3);
                    }
                }

                API.ScreenBuffer.Flush(Bounds.X, Bounds.Y, Bounds.Width, mnu.Height);
            }
            else
            {
                int offsetX = (menuHasIcons(mnu.Parent)) ? 20 : 0;
                int exW, exH;
                FontManager.Arial.ComputeExtent(">", out exW, out exH);

                if (mnu == _selMenu)
                {
                    API.ScreenBuffer.DrawRectangle(SelBottom, 0, Bounds.X - 2, Bounds.Y, Bounds.Width + 4, mnu.Height, 0, 0, SelBottom, 0, 0, SelBottom, 0, 0, Bitmap.OpacityOpaque);
                    API.ScreenBuffer.DrawText(mnu.Text, FontManager.Arial, Color.White, Bounds.X + offsetX, Bounds.Y + 2);
                    mnu.DrawIcon(API.ScreenBuffer, Bounds.X, Bounds.Y);
                    if (mnu.ItemCount > 0) API.ScreenBuffer.DrawText(">", FontManager.Arial, Color.White, Bounds.X + Bounds.Width - exW, Bounds.Y + 2);
                }
                else
                {
                    API.ScreenBuffer.DrawRectangle(NrmlTop, 0, Bounds.X - 2, Bounds.Y, Bounds.Width + 4, mnu.Height, 0, 0, NrmlTop, 0, 0, NrmlTop, 0, 0, Bitmap.OpacityOpaque);
                    API.ScreenBuffer.DrawText(mnu.Text, FontManager.Arial, (mnu.Enabled) ? Color.Black : Colors.Gray, Bounds.X + offsetX, Bounds.Y + 2);
                    mnu.DrawIcon(API.ScreenBuffer, Bounds.X, Bounds.Y);
                    if (mnu.ItemCount > 0) API.ScreenBuffer.DrawText(">", FontManager.Arial, (mnu.Enabled) ? Color.Black : Colors.Gray, Bounds.X + Bounds.Width - exW, Bounds.Y + 2);
                }


                API.ScreenBuffer.Flush(Bounds.X - 2, Bounds.Y, Bounds.Width + 4, mnu.Height);
            }

        }

        #endregion

        #region Internal Methods

        internal MenuItem GetItemAt(int index)
        {
            return (MenuItem)_MenuItems[index];
        }

        internal void ChildRequestedRender()
        {
            _parent.Render();
        }

        #endregion

        #region Private Methods

        /// <summary>
        /// Called to collapse all menu items
        /// </summary>
        private void collapseMenus(MenuItem skipCollapse)
        {
            MenuItem mnu;
            bool bRef = false;

            for (int i = 0; i < _MenuItems.Count; i++)
            {
                mnu = (MenuItem)_MenuItems[i];
                if (mnu.Expanded && mnu != skipCollapse)
                {
                    bRef = true;
                    mnu.Expanded = false;
                    collapseMenu(mnu, false);
                }
            }

            if (bRef) _parent.Render(true);
        }

        private bool collapseMenu(MenuItem mnu, bool flush)
        {
            bool bRef = false;

            for (int i = 0; i < mnu.ItemCount; i++)
            {
                if (mnu.Item(i).Expanded)
                {
                    mnu.Item(i).Expanded = false;
                    collapseMenu(mnu.Item(i), false);
                    bRef = true;
                }
            }

            if (bRef || flush)
            {
                _parent.Render(true);
                return true;
            }

            return false;
        }

        /// <summary>
        /// Collapse all sub menu items
        /// </summary>
        /// <param name="mnu"></param>
        private void collapseSubMenus(MenuItem mnu)
        {
            int i;
            MenuItem submnu;
            for (i = 0; i < mnu.ItemCount; i++)
            {
                submnu = mnu.Item(i);
                submnu.Bounds = new rect(0, 0, 0, 0);

                if (submnu.Expanded)
                {
                    if (submnu.ItemCount > 0)
                    {
                        collapseSubMenus(submnu);
                    }
                    submnu.Expanded = false;
                }
                if (submnu.ItemCount > 0) collapseSubMenus(submnu);
            }
        }

        /// <summary>
        /// Used to check which (if any) of the 
        /// MenuItems were affected by the Tap.
        /// </summary>
        /// <param name="mnu"></param>
        /// <param name="e"></param>
        /// <returns></returns>
        private MenuItem getClickedMenu(MenuItem mnu, point e)
        {
            int i;
            MenuItem submnu;
            MenuItem child;
            for (i = 0; i < mnu.ItemCount; i++)
            {
                submnu = mnu.Item(i);
                if (submnu.Enabled && submnu.Visible)
                {
                    if (submnu.Bounds.contains(e)) return submnu;
                    if (submnu.Expanded)
                    {
                        child = getClickedMenu(submnu, e);
                        if (child != null) return child;
                    }
                }
            }

            return null;
        }

        private rect getExpandedMenuRect(MenuItem mnu, int x, int y)
        {
            rect Bounds = new rect();

            MenuItem m;
            int i;
            int w = 0;  // Total menu width
            int h = 4;  // Total menu height

            int exW, exH;
            FontManager.Arial.ComputeExtent(">", out exW, out exH);

            // Calculate menu size
            for (i = 0; i < mnu.ItemCount; i++)
            {
                m = mnu.Item(i);
                if (m.Visible)
                {
                    if (m.Width > w) w = m.Width;
                    h += m.Height;
                }
            }
            w += 8;

            // Verify location
            // We don't want menus going off the screen
            if (x + w > AppearanceManager.ScreenWidth)
            {
                x = AppearanceManager.ScreenWidth - w;
                if (x < 0) x = 0;
            }
            if (y + h > AppearanceManager.ScreenHeight)
            {
                y = AppearanceManager.ScreenHeight - h;
                if (y < 21) y = 21;
            }

            Bounds.X = x;
            Bounds.Y = y;
            Bounds.Width = w;
            Bounds.Height = h;

            return Bounds;
        }

        /// <summary>
        /// Check to see if any menus in the group has an icon
        /// </summary>
        /// <param name="mnu">Parent menu</param>
        /// <returns>True if an icon is found</returns>
        private bool menuHasIcons(MenuItem mnu)
        {
            for (int i = 0; i < mnu.ItemCount; i++)
            {
                if (mnu.Item(i).icon != null && mnu.Item(i).Visible) return true;
            }
            return false;
        }

        #endregion

    }

}
